using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

namespace Kopain
{

    /// <summary>The class NoteRoot is the root of the Notes.  Duh.
    /// </summary>
    public partial class NoteRoot
    {

#region Private properties and constants.

        /// <summary>This is the name of the Note element.  It would be nice to validate it against the XSD file.  How do one do that?
        /// </summary>/// </summary>
        private const string _NoteElementName = "Note";

        /// <summary>This is the main root node.  It would be nice to validate it against the XSD file.  How do one do that?
        /// </summary>
        private const string _NoteRootElementName = "NoteRoot";

        /// <summary>This is the name of the openNoteId attribute of the NoteRoot element.  It would be nice to validate it against the XSD file.  How do one do that?
        /// </summary>
        private const string _NoteRootOpenNoteIdAttributeName = "openNoteId";

        /// <summary>This is the attribute for the version.  It would be nice to validate it against the XSD file.  How do one do that?
        /// </summary>
        private const string _NoteRootVersionAttributeName = "version";
        
#endregion  //  Private properties and constants.

#region Public methods.

        /// <summary>This method returns the Note by it's ID.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        internal NoteType GetNoteByID( string id )
        {
            return GetNoteByID( id, Note );
        }

        /// <summary>This method is the private recursive part of GetNoteByID.
        /// </summary>
        /// <param name="id"></param>
        /// <param name="notes"></param>
        /// <returns></returns>
        private NoteType GetNoteByID( string id, NoteType[] notes )
        {
            foreach( NoteType note in notes )
            {
                if( id == note.id )
                {
                    return note;
                }
                else
                {
                    return GetNoteByID( id, note.Note );
                }
            }
            return null;
        }

        /// <summary>This method adds a child to the Note.
        /// </summary>
        /// <returns>The newly created Note.</returns>
        internal NoteType AddChild()
        {
            //  Create a new Note and set some standard values.
            NoteType newNote = new NoteType();
            newNote.Set( this, null );

            //  Create an array if there isn't any.  We need something to add the new note to.
            if( null == this.Note )
            {
                this.Note = new NoteType[0];
            }

            //  Convert the array to a List<> to be able to add a new item to it.
            List<NoteType> lstSubNotes = new List<NoteType>( this.Note );
            lstSubNotes.Add( newNote );

            //  Copy the List<> back to the Note array.
            this.Note = lstSubNotes.ToArray();

            //  Return the newly created Note.
            return newNote;
        }

        /// <summary>This method is used for copying the structure of the list of the children back to the hierarchy.
        /// </summary>
        /// <param name="children"></param>
        internal void SetChildren( List<NoteType> children )
        {
            Note = children.ToArray();
        }

        /// <summary>This method returns the children/Notes of the Root as a list.
        /// This list is not connected to the hierachy, although the items are, so any
        /// changes in the list structure (add, remove, reorder...) must be commited
        /// back to the hierarcy by SetChildren.
        /// </summary>
        /// <returns></returns>
        internal List<NoteType> GetChildrenList()
        {
            return new List<NoteType>( Note );
        }

        /// <summary>This method retrieves all Notes in a depth-first fashion and returns them as a list.
        /// Great method for looping through all Notes.
        /// </summary>
        /// <returns></returns>
        internal List<NoteType> GetAllNotesDepthFirst()
        {
            List<NoteType> ret = new List<NoteType>();
            foreach( NoteType note in this.Note )
            {
                GetAllNotesDepthFirst( ret, note );
            }
            return ret;
        }

        /// <summary>This method returns an indented string to represent the Note hierarchy.  It also print to the debug output.
        /// </summary>
        /// <returns></returns>
        internal string ToDebugString()
        {
            int indentLevel = 0;
            string ret = "<root>\n";
            foreach (NoteType note in this.GetChildrenList())
            {
                //ret += LIUS.String.Helper.Space(indentLevel) + note.title + "\n";
                ret += note.ToDebugString(indentLevel + 1);
            }
            System.Diagnostics.Debug.WriteLine(ret);
            return ret;
        }

#endregion  //Public methods.

#region Private methods.

        /// <summary>This is a helper method for GetAllNotesDepthFirst.
        /// It recursively calls itself throughout the Notes tree.
        /// </summary>
        /// <param name="noteList"></param>
        /// <param name="note"></param>
        private void GetAllNotesDepthFirst( List<NoteType> noteList, NoteType note )
        {
            noteList.Add( note );
            if( null != note.Note )
            {
                foreach( NoteType n in note.Note )
                {
                    GetAllNotesDepthFirst( noteList, n );
                }
            }
        }

#endregion  //Private methods.

    }

    /// <summary>This class is the Note.
    /// </summary>
    public partial class NoteType
    {

#region Private properties and constants.

        /// <summary>This is the name of the cursorPosition attribute of the Note element.  It would be nice to validate it against the XSD file.  How do one do that?
        /// </summary>/// </summary>
        private const string _NoteCursorpositionAttributeName = "cursorPosition";

#endregion  //  Private properties and constants.

#region Private properties.

        /// <summary>Each Note has a parent unless they are directly under the root.
        /// </summary>
        private NoteType _parent;

        /// <summary>This property is a shortcut to the root.
        /// Maybe it should be changed to a method that iterates it's way up to the root instead.  Well... that cannot be done since
        /// a pParent which is a NoteTYpe is different from a root which is a NoteRoot.
        /// </summary>
        private NoteRoot _root;

#endregion  //Private properties.

#region Public properties.

        /// <summary>Moving a Note is done with these directions.
        /// </summary>
        internal enum MoveDirection
        {
            Up,
            Down,
            Left,
            Right
        }


        /// <summary>This method is a getter and setter for the parent of the Note.
        /// It is set to XmlIgnore so it won't be serialized since it is recursive.
        /// </summary>
        [XmlIgnore]
        public NoteType Parent
        {
            set { _parent = value; }
            get { return _parent; }
        }


        /// <summary>This method is a getter and setter for the root of the Note hierarchy.
        /// It is set to XmlIgnore so it won't be serialized since it is unneccessary to have X roots in the serialized document.
        /// </summary>
        [XmlIgnore]
        public NoteRoot Root
        {
            get { return _root; }
            set { _root = value; }
        }

#endregion  //Public properties.

#region Public methods.

        /// <summary>This returns the path and filename concatenated.
        /// </summary>
        internal string FileWithPath( NoteRoot root )
        {
            if( string.IsNullOrEmpty( System.IO.Path.GetDirectoryName( fileField ) ) )
            {
                return root.path + fileField;
            }
            else
            {
                return fileField;
            }
        }

        /// <summary>This is a getter for the index.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        internal NoteType this[string id]
        {
            get
            {
                foreach( NoteType note in this.Note )
                {
                    if( id == note.id )
                    {
                        return note;
                    }
                }
                throw new ArgumentOutOfRangeException( "id", id, "The id was not found." );
            }
            //set { List[id] = value; }
        }

        /// <summary>This method sets some standard values.  Use it when creating new Notes.
        /// </summary>
        internal void Set( NoteRoot root, NoteType parent )
        {
            this.id = Guid.NewGuid().ToString();
            this.file = ""; //  Set to store the text in the Savefile.
            this.Data = "";
            this.title = "new note";
            this.Root = root;
            this.Parent = parent;
        }

        /// <summary>This method adds a sibling Note to the Note.
        /// It returns the newly created Note.
        /// </summary>
        /// <returns></returns>
        internal NoteType AddSibling()
        {
            //  Create an array if there isn't any.  We need something to add the new note to.
            if( null == _parent )
            {
                //  Topmost notes.  So add a Child to the root.
                return _root.AddChild();
            }
            else
            {
                return _parent.AddChild();
            }
        }

        /// <summary>This method adds a child Note to the Note.
        /// It returns the newly created Note.
        /// </summary>
        /// <returns></returns>
        internal NoteType AddChild()
        {
            //  Create a new Note and set some standard values.
            NoteType newNote = new NoteType();
            newNote.Set( _root, this );

            //  Create an array if there isn't any.  We need something to add the new note to.
            if( null == this.Note )
            {
                this.Note = new NoteType[0];
            }

            //  Convert the array to a List<> to be able to add a new item to it.
            List<NoteType> lstSubNotes = new List<NoteType>( this.Note );
            lstSubNotes.Add( newNote );

            //  Copy the List<> back to the Note array.
            this.Note = lstSubNotes.ToArray();

            //  Return the newly created Note.
            return newNote;
        }

        /// <summary>This method deletes the Note.
        /// </summary>
        internal void Delete()
        {
            NoteType parentNote = this.Parent;
            //NoteType parentNote = noteRoot.GetParentOfNoteByID( this.id );
            if( null == parentNote )
            {
                //  Topmost Note.
                DeleteTopmostNote();
            }
            else
            {
                parentNote.DeleteChildNote( this );
            }
        }

        /// <summary>This method moves the note.
        /// </summary>
        /// <param name="direction"></param>
        internal void Move( MoveDirection direction )
        {
            switch( direction )
            {
                case MoveDirection.Up:
                    {
                        List<NoteType> siblings = GetSiblingsList();
                        LIUS.Templates.List.ListHelper.MoveLeft<NoteType>( siblings, this );
                        SetSiblings( siblings );
                        //TODO:Throw exception when moving the topmost up.
                    }
                    break;
                case MoveDirection.Down:
                    {
                        List<NoteType> siblings = GetSiblingsList();
                        LIUS.Templates.List.ListHelper.MoveRight<NoteType>( siblings, this );
                        SetSiblings( siblings );
                        //TODO:Throw exception when throwing the bottommost down.
                    }
                    break;
                case MoveDirection.Left:
                    {
                        if( IsTopmost() )
                        {
                            throw new Exception.IllegalDirection( direction );
                        }
                        else
                        {
                            NoteType targetParent = this.Parent.Parent;   //  Null if the node is one level down.
                            NoteType target = this.Parent; // Target is the one to get a new sibling.
                            List<NoteType> targetSiblings = target.GetSiblingsList(); // (null == targetParent) ? Root.GetChildrenList() : targetParent.GetSiblingsList();
                            List<NoteType> siblings = GetSiblingsList();   //  Siblings to the node we are moving.
                            siblings.Remove( this );
                            SetSiblings( siblings );
                            this.Parent = targetParent;
                            targetSiblings.Insert(target.GetIndex() + 1, this);
                            if( null == Parent )
                            {
                                Root.SetChildren( targetSiblings );
                            }
                            else
                            {
                                targetParent.SetChildren( targetSiblings );
                            }
                        }
                    }
                    break;
                case MoveDirection.Right:
                    {
                        if( IsFirstSibling() )

                        {
                            throw new Exception.IllegalDirection( direction );
                        }
                        else
                        {
                            NoteType targetParent = GetSiblings()[GetIndex() - 1];
                            List<NoteType> targetSiblings = targetParent.GetChildrenList();
                            List<NoteType> siblings = GetSiblingsList();
                            siblings.Remove( this );
                            SetSiblings( siblings );
                            this.Parent = targetParent;
                            targetSiblings.Add( this );
                            targetParent.SetChildren( targetSiblings );
                        }
                    }
                    break;
                default:
                    //throw new ArgumentOutOfRangeException( "direction", direction, "The direction is not handled." );
                    break;
            }
        }

        /// <summary>This method returns true if the Note is the first among equals.
        /// </summary>
        /// <returns></returns>
        private bool IsFirstSibling()
        {
            return ( 0 == this.GetIndex() );
        }

        /// <summary>This method returns true if the Note is a child directly to the root.
        /// </summary>
        /// <returns></returns>
        internal bool IsTopmost()
        {
            return (null == this.Parent);
        }

        /// <summary>This method is used for copying the structure of the list of the siblings back to the hierarchy.
        /// </summary>
        /// <param name="siblings"></param>
        internal void SetSiblings( List<NoteType> siblings )
        {
            if( IsTopmost() )
            {
                this.Root.Note = siblings.ToArray();
            }
            else
            {
                this.Parent.Note = siblings.ToArray();
            }
        }

        /// <summary>This method is used for copying the structure of the list of the children back to the hierarchy.
        /// </summary>
        /// <param name="children"></param>
        internal void SetChildren( List<NoteType> children )
        {
            this.Note = children.ToArray();
        }

        /// <summary>This method returns the siblings of a Note.
        /// </summary>
        /// <returns></returns>
        internal NoteType[] GetSiblings()
        {
            if( IsTopmost() )
            {
                return this.Root.Note;
            }
            else
            {
                return this.Parent.Note;
            }
        }

        /// <summary>This method returns the siblings/Notes of the Note as a list.
        /// This list is not connected to the hierachy, although the items are, so any
        /// changes in the list structure (add, remove, reorder...) must be commited
        /// back to the hierarcy by SetChildren.
        /// </summary>
        /// <returns></returns>
        internal List<NoteType> GetSiblingsList()
        {
            return new List<NoteType>( GetSiblings() );
        }

        /// <summary>This method returns the children/Notes of the Note as a list.
        /// This list is not connected to the hierachy, although the items are, so any
        /// changes in the list structure (add, remove, reorder...) must be commited
        /// back to the hierarcy by SetChildren.
        /// </summary>
        /// <returns></returns>
        internal List<NoteType> GetChildrenList()
        {
            if( null == Note )
            {
                return new List<NoteType>();
            }
            else
            {
                return new List<NoteType>( Note );
            }
        }

        /// <summary>This method returns an indented string to represent the Note hierarchy.  It is probably this.Root.ToDebugString() you should call.
        /// </summary>
        /// <param name="indentLevel"></param>
        /// <returns></returns>
        internal string ToDebugString(int indentLevel)
        {
            string ret = LIUS.String.Helper.Space(4 * indentLevel) + this.title +  "\n";
            foreach (NoteType note in this.GetChildrenList())
            {
                ret += note.ToDebugString(indentLevel+1);
            }
            return ret;
        }

#endregion  //Public methods.

#region Private methods.

        /// <summary>This method returns the ordinal, the index, of the sibling.
        /// </summary>
        /// <returns></returns>
        private int GetIndex()
        {
            List<NoteType> siblings = GetSiblingsList();
            for( int i = 0 ; i < siblings.Count ; i++ )
            {
                if( this == siblings[i] ) { 
                    return i; 
                }
            }
            //  It cannot come here.  I hope.  The only reason I write this is the far fetched idea that the GetSiblings method
            //  doesn't work.
            //System.Diagnostics.Debug.Assert( false );
            throw new ApplicationException( "Internal error.  The Note doesn't seem to be in any collection." );
        }


        /// <summary>This method deletes the object this.  this must be directly under the root.
        /// </summary>
        private void DeleteTopmostNote()
        {
            List<NoteType> lst = new List<NoteType>( this.Root.Note );
            lst.Remove( this );
            this.Root.Note = lst.ToArray();
        }


        /// <summary>This method deletes a child Note.
        /// </summary>
        /// <param name="note"></param>
        private void DeleteChildNote( NoteType note )
        {
            List<NoteType> lst = new List<NoteType>( this.Note );
            lst.Remove( note );
            this.noteField = lst.ToArray();
        }

#endregion  //Private methods.

    }

}
